package com.df.library.net;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * 自定义支持的协议
 */
@Slf4j
public class CommonServer {

    /**
     * 服务名
     */
    @Setter
    @Getter
    private String name;

    /**
     * 绑定的端口
     */
    private List<Integer> ports = new ArrayList<>();
    /**
     * 服务器channel的额外处理器
     */
    @Setter
    @Getter
    private ChannelHandler bossChannelHandler;

    /**
     * 连接建立时的初始化器
     */
    @Setter
    private ChannelInitializer<SocketChannel> channelInitializer;


    private ServerBootstrap bootstrap;
    private EventLoopGroup boss;
    private EventLoopGroup work;

    private AtomicBoolean running = new AtomicBoolean(false);

    private ReentrantLock lock = new ReentrantLock();

    public CommonServer(){}


    public boolean addListenPort(int port){
        if(ports.contains(port)){
            log.error("端口 "+port+"已经被监听过了，不能重复监听！服务名："+name);
            return false;
        }
        ports.add(port);
        return true;
    }

    public boolean isRunning(){
        return running.get();
    }

    /**
     * 初始化操作
     */
    private void init(){
        bootstrap = new ServerBootstrap();
        boss = getEventLoopGroup();
        work = getEventLoopGroup();

        bootstrap.group(boss, work)
                .channel(getChannelClass())
                .option(ChannelOption.SO_BACKLOG,256)
                .childOption(ChannelOption.SO_KEEPALIVE,true)
                .childOption(ChannelOption.TCP_NODELAY,true);
        if(bossChannelHandler!=null){
            bootstrap.handler(bossChannelHandler);
        }
        bootstrap.childHandler(channelInitializer);

    }

    public EventLoopGroup getEventLoopGroup(){
        EventLoopGroup boss = null;
        String osName = System.getProperty("os.name").toLowerCase();
        if(osName.startsWith("linux")){
            boss = new EpollEventLoopGroup();
        }else {
            boss = new NioEventLoopGroup();
        }
        return boss;
    }

    public Class<? extends ServerSocketChannel> getChannelClass(){
        String osName = System.getProperty("os.name").toLowerCase();
        if(osName.startsWith("linux")){
            return EpollServerSocketChannel.class;
        }else {
            return NioServerSocketChannel.class;
        }
    }

    public boolean start(){
        return start(null);
    }
    public boolean start(BiConsumer<CommonServer,Channel> afterStart){
        if(running.get()){
            return true;
        }
        if(ports.isEmpty()){
            log.error("服务："+name+" 未设置监听端口，启动失败！");
            return false;
        }
        if(channelInitializer==null){
            log.error("服务："+name+" channelInitializer为null，启动失败！");
            return false;
        }
        lock.lock();
        try {
            if(running.get()){
                return true;
            }
            init();
            ChannelFuture sync = null;
            for (Integer port : ports) {
                sync = bootstrap.bind(port).sync();
                log.info(name+" 监听 "+port+" 成功！");
            }
            ServerSocketChannel channel = (ServerSocketChannel) sync.channel();
            if(afterStart!=null){
                afterStart.accept(this,channel);
            }
            running.set(true);
        }catch (Exception e){
            if(boss!=null){
                boss.shutdownGracefully();
            }
            if(work!=null){
                work.shutdownGracefully();
            }
            log.error("服务 "+name+" 启动失败！",e);
            return false;
        }finally {
            lock.unlock();
        }
        return true;
    }

    public boolean stop(){
        if(!running.get()){
            return true;
        }
        lock.lock();
        try {
            if(boss!=null){
                boss.shutdownGracefully();
            }
            if(work!=null){
                work.shutdownGracefully();
            }

        }finally {
            lock.unlock();
        }
        return true;
    }
}
